/**
 * @file FSPLanguage4.h
 * @author Cosimo Sacco <cosimosacco@gmail.com>
 *
 * @section LICENSE
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 **/

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/home/phoenix/bind/bind_member_function.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <string>
#include "ExitException.h"
using namespace std;
using namespace boost::spirit;
using namespace boost::spirit::qi;
using namespace boost::phoenix;

#ifndef FSP_LANGUAGE_H
#define FSP_LANGUAGE_H

//TODO higher abstraction of the language concept, something like:
//a Language is something composed by a Syntax, a Semantic and relations between
//those objects.

/**
 * @class FSPLanguage
 * @brief A simple language to control the application.
 * PantaReiLanguage defines the language to interact with the application using
 * EBNF through the Boost Spirit Qi parsing framework. While the language syntax
 * is defined here, the language semantics are defined into the
 * SchedulingSimulation class, whose methods are called as semantic actions.
 * @see Boost Spirit Qi, Boost Phoenix
 **/
class FSPLanguage :
  public
  boost::spirit::qi::grammar
    <
        std::string::const_iterator,
        boost::spirit::ascii::space_type
    >
{
  private:
    ExitException exitSignal;
    void callable(std::string message){/*std::cerr << message << std::endl;*/}
    void printer(std::string message){/*std::cerr << message << std::endl;*/}
    void exit(){throw exitSignal;}
    /** This help method shows an EBNF description of the language. **/
    void help(){std::cout << "Lo rifamo" << std::endl;}
  public:
  /**
   * The syntax tree is created at construction time: the starting symbol
   * is set through the initialization of base_type (inherited from
   * boost::spirit::qi::grammar<std::string::const_iterator,
   * boost::spirit::ascii::space_type>), while the rest of the tree is
   * created using Boost Spirit Qi magics. Terminal symbols are simple
   * parsers, while non-terminal symbols are boost::spirit::qi::rule
   * <std::string::const_iterator, boost::spirit::ascii::space_type>.
   **/
  FSPLanguage() : FSPLanguage::base_type(TestSymbol)
  {
    TestSymbol %= (ActionLabels)
      [bind(&FSPLanguage::callable, *this, "TEST")]
    ;
    FSPDescription %=
      (*FSPDefinition)
      [bind(&FSPLanguage::callable, *this, "FSPDescription")]
    ;
    FSPDefinition %=
      (
        ConstantDefinition  |
        RangeDefinition     |
        SetDefinition       |
        ProcessDefinition   |
        CompositeDefinition |
        PropertyDefinition  |
        ProgressDefinition
      )
      [bind(&FSPLanguage::callable, *this, "FSPDefinition")]
    ;
    ConstantDefinition %=
      (lit("const") >> ConstantIdentifier >> lit("=") >> SimpleExpression)
      [bind(&FSPLanguage::callable, *this, "ConstantDefinition")]
    ;
    RangeDefinition %=
      (
        lit("range") >> RangeIdentifier >> lit("=") >>
        SimpleExpression >> lit("..") >> SimpleExpression
      )
      [bind(&FSPLanguage::callable, *this, "RangeDefinition")]
    ;
    SetDefinition %=
      (
        lit("set") >> SetIdentifier >> lit("=") >>
        lit("{") >> SetElements >> lit("}")
      )
      [bind(&FSPLanguage::callable, *this, "SetDefinition")]
    ;
    ProcessDefinition %=
      (
        ProcessIdentifier >> (-Parameter) >> lit("=") >> ProcessBody >>
        (-AlphabetExtension) >> (-Relabel) >> (-Hiding) >> lit(".")
      )
      [bind(&FSPLanguage::callable, *this, "ProcessDefinition")]
    ;
    ProcessBody %= (LocalProcess >> *(lit(",") >> LocalProcessDefinition))
      [bind(&FSPLanguage::callable, *this, "ProcessBody")]
    ;
    LocalProcessDefinition %=
      (ProcessIdentifier >> (-IndexRanges) >> lit("=") >> LocalProcess)
      [bind(&FSPLanguage::callable, *this, "LocalProcessDefinition")]
    ;
    AlphabetExtension %=
      (lit("+") >> Set)
      [bind(&FSPLanguage::callable, *this, "AlphabetExtension")];
    LocalProcess %=
      (
        BaseLocalProcess                                       |
        SequentialComposition                                  |
        lit("if") >> Expression >> lit("then") >> LocalProcess |
        lit("if") >> Expression >> lit("then") >> LocalProcess
                  >> lit("else") >> LocalProcess               |
        lit("(") >> Choice >> lit(")")
      )
      [bind(&FSPLanguage::callable, *this, "LocalProcess")]
    ;
    BaseLocalProcess %=
      (
        lit("END") | lit("STOP") | lit("ERROR") |
        ProcessIdentifier >> (-Indices)
      )
      [bind(&FSPLanguage::callable, *this, "BaseLocalProcess")]
    ;
    Choice %=
      (ActionPrefix % lit("|"))
      [bind(&FSPLanguage::callable, *this, "Choice")]
    ;
    ActionPrefix %=
      ((-Guard) >> PrefixActions >> lit("->") >> LocalProcess)
      [bind(&FSPLanguage::callable, *this, "ActionPrefix")]
    ;
    PrefixActions %=
      (ActionLabels % lit("->"))
      [bind(&FSPLanguage::callable, *this, "PrefixActions")]
    ;
    Guard %=
      (lit("when") >> Expression)
      [bind(&FSPLanguage::callable, *this, "Guard")]
    ;
    ActionLabels %= //Set of ActionLabel written in a succint form
      (ActionLabel | Set | (lit("[") >> ActionRange >> lit("]"))) >>
      (
        *(
          (lit(".") >> ActionLabel) |
          (lit(".") >> Set        ) |
          (lit("[") >> ActionRange >> lit("]")) |
          (lit("[") >> Expression >> lit("]"))
        )
      )
      [bind(&FSPLanguage::callable, *this, "ActionLabels")]
    ;
    ActionLabel %=
      (
        (LowercaseIdentifier | (lit("[") >> Expression >> lit("]"))) >>
        (*
          (
            (lit(".") >> LowercaseIdentifier) |
            (lit("[") >> Expression >> lit("]"))
          )
        )
      )
      [bind(&FSPLanguage::callable, *this, "ActionLabels")]
    ;
    ActionRange %=
      (
        Set                           |
        Range                         |
        (Variable >> lit(":") >> Set)   |
        (Variable >> lit(":") >> Range)
      );
    Range %= (RangeIdentifier | Expression >> lit("..") >> Expression);
    Set %= (SetIdentifier | lit("{") >> SetElements >> lit("}"));
    SetElements %= (ActionLabels % lit(","));
    Indices %= ((lit("[") >> Expression >> lit("]")) % eps);
    IndexRanges %=
      ((lit("[") >> (Expression | ActionRange) >> lit("]")) % eps);
    SequentialComposition %=
      (SequentialProcessList >> lit(";") >> BaseLocalProcess);
    SequentialProcessList %= (ProcessReference % lit(";"));
    ProcessReference %= (ProcessIdentifier >> (-Argument));
    Argument %= (lit("(") >> ArgumentList >> lit(")"));
    ArgumentList %= (Expression % lit(","));
    CompositeDefinition %=
      (
        lit("||") >> ProcessIdentifier >> (-Parameter) >> lit("=") >>
        CompositeBody >> (-Priority) >> (-Hiding) >> lit(".")
      )
    ;
    CompositeBody %=
      (
        lit("forall") >> Ranges >> CompositeBody                |
        (-PrefixLabel) >> ProcessReference >> (-Relabel)        |
        (-PrefixLabel) >> lit("(") >> ParallelComposition >>
        lit(")") >> (-Relabel)                                  |
        lit("if") >> Expression >> lit("then") >> CompositeBody |
        lit("if") >> Expression >>
        lit("then") >> CompositeBody >>
        lit("else") >> CompositeBody
      )
    ;
    PrefixLabel %=
      (
        ActionLabels >> lit(":")                              |
        ActionLabels >> lit("::")                             |
        ActionLabels >> lit("::") >> ActionLabels >> lit(":")
      )
    ;
    ParallelComposition %=
      (CompositeBody % eps)
    ;
    Priority %=
      (lit("<<") >> Set | lit(">>") >> Set)
    ;
    Ranges %=
      (lit("[") >> ActionRange >> lit("]") % eps)
    ;
    Parameter %=
      (lit("(") >> ParameterList >> lit(")"))
    ;
    ParameterList %=
      (ParameterElement % lit(","))
    ;
    ParameterElement %=
      (ParameterIdentifier >> lit("=") >> Expression)
    ;
    Relabel %=
      (lit("/") >> lit("{") >> RelabelDefinitions >> lit("}"))
    ;
    RelabelDefinitions %=
      (RelabelDefinition % lit(","))
    ;
    RelabelDefinition %=
      (
        ActionLabels >> lit("/") >> ActionLabels   |
        lit("forall") >> IndexRanges >>
        lit("{") >> RelabelDefinitions >> lit("}")
      )
    ;
    Hiding %=
      (lit("\\") >> Set | lit("@") >> Set)
    ;
    PropertyDefinition %=
      (lit("property") >> ProcessDefinition)
      ;
    ProgressDefinition %=
      (
        lit("progress") >> ProgressIdentifier >> (-Ranges) >> lit("=") >>
        (Set /*| (lit("if") >> Set >> lit("then") >> Set)*/)
      )
    ;
    SimpleExpression %= AdditiveExpression[_val = _1];
    Expression %= OrExpression[_val = _1];
    OrExpression %=
      AndExpression[_val = _1] >>
      *(lit("||") >> AndExpression[_val = _val || _1])
    ;
    AndExpression %=
      BitOrExpression[_val = _1] >>
      *(lit("&&") >> BitOrExpression[_val = _val && _1])
    ;
    BitOrExpression %=
      BitExclusiveOrExpression[_val = _1] >>
      *(lit("|") >> BitExclusiveOrExpression[_val |= _1])
    ;
    BitExclusiveOrExpression %=
      BitAndExpression[_val = _1] >>
      *(lit("^") >> BitAndExpression[_val ^= _1])
    ;
    BitAndExpression %=
      EqualityExpression[_val = _1] >>
      *(lit("&") >> EqualityExpression[_val &= _1])
    ;
    EqualityExpression %=
      RelationalExpression[_val = _1] >>
      *(
        (lit("==") >> RelationalExpression[_val = _val == _1]) |
        (lit("!=") >> RelationalExpression[_val = _val != _1])
      )
    ;
    RelationalExpression %=
      ShiftExpression[_val = _1] >>
      *(
        (lit("<" ) >> ShiftExpression[_val = _val <  _1]) |
        (lit("<=") >> ShiftExpression[_val = _val <= _1]) |
        (lit(">" ) >> ShiftExpression[_val = _val >  _1]) |
        (lit(">=") >> ShiftExpression[_val = _val >= _1])
      )
    ;
    ShiftExpression %=
      AdditiveExpression[_val = _1] >>
      *(
        (lit("<<") >> AdditiveExpression[_val <<= _1]) |
        (lit(">>") >> AdditiveExpression[_val >>= _1])
      )
    ;
    AdditiveExpression %=
      MultiplicativeExpression[_val = _1] >>
      *(
        (lit("+") >> MultiplicativeExpression[_val += _1]) |
        (lit("-") >> MultiplicativeExpression[_val -= _1])
      )
      ;
    MultiplicativeExpression %=
      UnaryExpression[_val = _1] >>
      *(
        (lit("*") >> UnaryExpression[_val *= _1]) |
        (lit("/") >> UnaryExpression[_val /= _1]) |
        (lit("%") >> UnaryExpression[_val %= _1])
      )
    ;
    UnaryExpression %=
      ((-lit("+")) >> BaseExpression)[_val =  _1] |
      (  lit("-")  >> BaseExpression)[_val = -_1] |
      (  lit("!")  >> BaseExpression)[_val = !_1]
    ;
    BaseExpression %=
      (
        int_[_val = _1] |
        (lit("(") >> Expression[_val = _1] >> lit(")"))
        /*Variable [_val = -999] |
        ConstantIdentifier [_val = -999] |
        lit("'") >> ActionLabel | ATTENZIONE QUI: richiederebbe Actionlabel(!s)!
        (lit("#") >> SetIdentifier) [_val = -999]|
        (lit("@") >>
          (lit("(") >> SetIdentifier >> lit(",") >> Expression >>lit(")"))
        )[_val = -999]*/
      )
      /*[bind(&FSPLanguage::printer, *this, boost::lexical_cast<std::string>(_val))]*/
    ;
    ConstantIdentifier %=
      UppercaseIdentifier
      [bind(&FSPLanguage::callable, *this, "ConstantIdentifier")]
    ;
    RangeIdentifier %=
      UppercaseIdentifier
      [bind(&FSPLanguage::callable, *this, "RangeIdentifier")]
    ;
    SetIdentifier %=
      UppercaseIdentifier
      [bind(&FSPLanguage::callable, *this, "SetIdentifier")]
    ;
    ParameterIdentifier %=
      UppercaseIdentifier
      [bind(&FSPLanguage::callable, *this, "ParameterIdentifier")]
    ;
    ProcessIdentifier %=
      UppercaseIdentifier
      [bind(&FSPLanguage::callable, *this, "ProcessIdentifier")]
    ;
    PropertyIdentifier %=
      UppercaseIdentifier
      [bind(&FSPLanguage::callable, *this, "PropertyIdentifier")]
    ;
    ProgressIdentifier %=
      UppercaseIdentifier
      [bind(&FSPLanguage::callable, *this, "ProgressIdentifier")]
    ;
    Variable %=
      LowercaseIdentifier
      [bind(&FSPLanguage::callable, *this, "VariableIdentifier")]
    ;
    UppercaseIdentifier %=
      (lexeme[char_("A-Z") >> *char_("a-zA-Z0-9")])
      [bind(&FSPLanguage::callable, *this, "UppercaseIdentifier")]
    ;
    LowercaseIdentifier %=
      (lexeme[char_("a-z") >> *char_("a-zA-Z0-9")])
      [bind(&FSPLanguage::callable, *this, "LowercaseIdentifier")]
    ;
    /*<debug>*/
    TestSymbol.name("TestSymbol");
    debug(TestSymbol);
    FSPDescription.name("FSPDescription");
    debug(FSPDescription);
    FSPDefinition.name("FSPDefinition");
    debug(FSPDefinition);
    ConstantDefinition.name("ConstantDefinition");
    debug(ConstantDefinition);
    RangeDefinition.name("RangeDefinition");
    debug(RangeDefinition);
    SetDefinition.name("SetDefinition");
    debug(SetDefinition);
    ProcessDefinition.name("ProcessDefinition");
    debug(ProcessDefinition);
    ProcessBody.name("ProcessBody");
    debug(ProcessBody);
    LocalProcess.name("LocalProcess");
    debug(LocalProcess);
    CompositeDefinition.name("CompositeDefinition");
    debug(CompositeDefinition);
    PropertyDefinition.name("PropertyDefinition");
    debug(PropertyDefinition);
    ProgressDefinition.name("ProgressDefinition");
    debug(ProgressDefinition);
    ConstantIdentifier.name("ConstantIdentifier");
    debug(ConstantIdentifier);
    RangeIdentifier.name("RangeIdentifier");
    debug(RangeIdentifier);
    SetIdentifier.name("SetIdentifier");
    debug(SetIdentifier);
    ParameterIdentifier.name("ParameterIdentifier");
    debug(ParameterIdentifier);
    ProcessIdentifier.name("ProcessIdentifier");
    debug(ProcessIdentifier);
    PropertyIdentifier.name("PropertyIdentifier");
    debug(PropertyIdentifier);
    ProgressIdentifier.name("ProgressIdentifier");
    debug(ProgressIdentifier);
    UppercaseIdentifier.name("UppercaseIdentifier");
    debug(UppercaseIdentifier);
    Parameter.name("Parameter");
    debug(Parameter);
    Range.name("Range");
    debug(Range);
    Set.name("Set");
    debug(Set);
    Variable.name("Variable");
    debug(Variable);
    SetElements.name("SetElements");
    debug(SetElements);
    LowercaseIdentifier.name("LowercaseIdentifier");
    debug(LowercaseIdentifier);
    ActionLabels.name("ActionLabels");
    debug(ActionLabels);
    ActionLabel.name("ActionLabel");
    debug(ActionLabel);
    ActionRange.name("ActionRange");
    debug(ActionRange);
    Expression.name("Expression");
    debug(Expression);
    SimpleExpression.name("SimpleExpression");
    debug(SimpleExpression);
    AdditiveExpression.name("AdditiveExpression");
    debug(AdditiveExpression);
    OrExpression.name("OrExpression");
    debug(OrExpression);
    AndExpression.name("AndExpression");
    debug(AndExpression);
    BitOrExpression.name("BitOrExpression");
    debug(BitOrExpression);
    BitExclusiveOrExpression.name("BitExclusiveOrExpression");
    debug(BitExclusiveOrExpression);
    BitAndExpression.name("BitAndExpression");
    debug(BitAndExpression);
    EqualityExpression.name("EqualityExpression");
    debug(EqualityExpression);
    RelationalExpression.name("RelationalExpression");
    debug(RelationalExpression);
    ShiftExpression.name("ShiftExpression");
    debug(ShiftExpression);
    ParameterList.name("ParameterList");
    debug(ParameterList);
    MultiplicativeExpression.name("MultiplicativeExpression");
    debug(MultiplicativeExpression);
    UnaryExpression.name("UnaryExpression");
    debug(UnaryExpression);
    BaseExpression.name("BaseExpression");
    debug(BaseExpression);
    AlphabetExtension.name("AlphabetExtension");
    debug(AlphabetExtension);
    Relabel.name("Relabel");
    debug(Relabel);
    LocalProcessDefinition.name("LocalProcessDefinition");
    debug(LocalProcessDefinition);
    BaseLocalProcess.name("BaseLocalProcess");
    debug(BaseLocalProcess);
    SequentialComposition.name("SequentialComposition");
    debug(SequentialComposition);
    Choice.name("Choice");
    debug(Choice);
    Indices.name("Indices");
    debug(Indices);
    ActionPrefix.name("ActionPrefix");
    debug(ActionPrefix);
    Guard.name("Guard");
    debug(Guard);
    PrefixActions.name("PrefixActions");
    debug(PrefixActions);
    SequentialProcessList.name("SequentialProcessList");
    debug(SequentialProcessList);
    ProcessReference.name("ProcessReference");
    debug(ProcessReference);
    Argument.name("Argument");
    debug(Argument);
    ArgumentList.name("ArgumentList");
    debug(ArgumentList);
    CompositeBody.name("CompositeBody");
    debug(CompositeBody);
    Priority.name("Priority");
    debug(Priority);
    Hiding.name("Hiding");
    debug(Hiding);
    PrefixLabel.name("PrefixLabel");
    debug(PrefixLabel);
    Ranges.name("Ranges");
    debug(Ranges);
    ParallelComposition.name("ParallelComposition");
    debug(ParallelComposition);
    ParameterElement.name("ParameterElement");
    debug(ParameterElement);
    RelabelDefinitions.name("RelabelDefinitions");
    debug(RelabelDefinitions);
    RelabelDefinition.name("RelabelDefinition");
    debug(RelabelDefinition);
    IndexRanges.name("IndexRanges");
    debug(IndexRanges);
    /*</debug>*/
  }
  rule<string::const_iterator, boost::spirit::ascii::space_type>
    FSPDescription, FSPDefinition, ConstantDefinition, RangeDefinition,
    SetDefinition, ProcessDefinition, ProcessBody, LocalProcess,
    CompositeDefinition, PropertyDefinition,
    ProgressDefinition, ConstantIdentifier, RangeIdentifier, SetIdentifier,
    ParameterIdentifier, ProcessIdentifier, PropertyIdentifier,
    ProgressIdentifier, UppercaseIdentifier, Parameter, Range, Set, Variable,
    SetElements, LowercaseIdentifier, ActionLabels, ActionLabel, ActionRange,
    ParameterList,
    AlphabetExtension, Relabel, LocalProcessDefinition, BaseLocalProcess,
    SequentialComposition, Choice, Indices, ActionPrefix, Guard, PrefixActions,
    SequentialProcessList, ProcessReference, Argument, ArgumentList,
    CompositeBody, Priority, Hiding, PrefixLabel, Ranges, ParallelComposition,
    ParameterElement, RelabelDefinitions, RelabelDefinition, IndexRanges,
    TestSymbol;
  rule<string::const_iterator, int(), boost::spirit::ascii::space_type>
    BaseExpression, UnaryExpression, MultiplicativeExpression, AdditiveExpression,
    ShiftExpression, RelationalExpression, EqualityExpression, BitAndExpression,
    BitExclusiveOrExpression, Expression, SimpleExpression, OrExpression,
    AndExpression, BitOrExpression;
};

#endif
